This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

source("tianfengRwrappers.R")
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
载入需要的程辑包:dplyr

载入程辑包:‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

载入需要的程辑包:reticulate
载入需要的程辑包:tidyr

载入程辑包:‘MySeuratWrappers’

The following objects are masked from ‘package:Seurat’:

    DimPlot, DoHeatmap, LabelClusters, RidgePlot, VlnPlot


载入程辑包:‘cowplot’

The following object is masked from ‘package:ggpubr’:

    get_legend

载入需要的程辑包:viridisLite

载入程辑包:‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths

NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
      Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
      if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow

Registered S3 method overwritten by 'enrichplot':
  method               from
  fortify.enrichResult DOSE
clusterProfiler v3.14.3  For help: https://guangchuangyu.github.io/software/clusterProfiler

If you use clusterProfiler in published research, please cite:
Guangchuang Yu, Li-Gen Wang, Yanyan Han, Qing-Yu He. clusterProfiler: an R package for comparing biological themes among gene clusters. OMICS: A Journal of Integrative Biology. 2012, 16(5):284-287.
Registering fonts with R

载入程辑包:‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

载入需要的程辑包:Biobase
载入需要的程辑包:BiocGenerics
载入需要的程辑包:parallel

载入程辑包:‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap,
    parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB

The following objects are masked from ‘package:dplyr’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call,
    duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted,
    lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply, union,
    unique, unsplit, which, which.max, which.min

Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite
    Bioconductor, see 'citation("Biobase")', and for packages 'citation("pkgname")'.

载入需要的程辑包:e1071

载入程辑包:‘widgetTools’

The following object is masked from ‘package:dplyr’:

    funs


载入程辑包:‘DynDoc’

The following object is masked from ‘package:BiocGenerics’:

    path


载入程辑包:‘DT’

The following object is masked from ‘package:Seurat’:

    JS

========================================
circlize version 0.4.13
CRAN page: https://cran.r-project.org/package=circlize
Github page: https://github.com/jokergoo/circlize
Documentation: https://jokergoo.github.io/circlize_book/book/

If you use it in published research, please cite:
Gu, Z. circlize implements and enhances circular visualization
  in R. Bioinformatics 2014.

This message can be suppressed by:
  suppressPackageStartupMessages(library(circlize))
========================================

载入需要的程辑包:grid
========================================
ComplexHeatmap version 2.2.0
Bioconductor page: http://bioconductor.org/packages/ComplexHeatmap/
Github page: https://github.com/jokergoo/ComplexHeatmap
Documentation: http://jokergoo.github.io/ComplexHeatmap-reference

If you use it in published research, please cite:
Gu, Z. Complex heatmaps reveal patterns and correlations in multidimensional 
  genomic data. Bioinformatics 2016.
========================================


载入程辑包:‘ComplexHeatmap’

The following object is masked from ‘package:plotly’:

    add_heatmap
library(xgboost)

载入程辑包:‘xgboost’

The following object is masked from ‘package:plotly’:

    slice

The following object is masked from ‘package:dplyr’:

    slice
library(Matrix)

载入程辑包:‘Matrix’

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack
library(mclust)
    __  ___________    __  _____________
   /  |/  / ____/ /   / / / / ___/_  __/
  / /|_/ / /   / /   / / / /\__ \ / /   
 / /  / / /___/ /___/ /_/ /___/ // /    
/_/  /_/\____/_____/\____//____//_/    version 5.4.9
Type 'citation("mclust")' for citing this R package in publications.
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
Registered S3 method overwritten by 'cli':
  method     from    
  print.boxx spatstat
─ Attaching packages ─────────────────────────────── tidyverse 1.3.1 ─
✓ tibble  3.1.5     ✓ stringr 1.4.0
✓ readr   2.0.2     ✓ forcats 0.5.1
✓ purrr   0.3.4     
─ Conflicts ───────────────────────────────── tidyverse_conflicts() ─
x Biobase::combine()       masks BiocGenerics::combine(), dplyr::combine()
x Matrix::expand()         masks tidyr::expand()
x plotly::filter()         masks dplyr::filter(), stats::filter()
x widgetTools::funs()      masks dplyr::funs()
x dplyr::lag()             masks stats::lag()
x purrr::map()             masks mclust::map()
x Matrix::pack()           masks tidyr::pack()
x BiocGenerics::Position() masks ggplot2::Position(), base::Position()
x purrr::simplify()        masks clusterProfiler::simplify()
x xgboost::slice()         masks plotly::slice(), dplyr::slice()
x Matrix::unpack()         masks tidyr::unpack()

数值化

ds2训练分类器


ds2_data <- get_data_table(ds2, highvar = F, type = "data")
ds2_label <- as.numeric(as.character(Idents(ds2)))

index <- c(1:dim(ds2_data)[2]) %>% sample(ceiling(0.3*dim(ds2_data)[2]), replace = F, prob = NULL)
colnames(ds2_data) <- NULL

ds2_train_data <- list(data = t(as(ds2_data[,-index],"dgCMatrix")), label = ds2_label[-index])
ds2_test_data <- list(data = t(as(ds2_data[,index],"dgCMatrix")), label = ds2_label[index])

ds2_train <- xgb.DMatrix(data = ds2_train_data$data,label = ds2_train_data$label)
ds2_test <- xgb.DMatrix(data = ds2_test_data$data,label = ds2_test_data$label)

watchlist <- list(train = ds2_train, eval = ds2_test)
xgb_param <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds2))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_param, ds2_train, nrounds = 100, watchlist, verbose = 0)

eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>% 
  add_trace(type = "scatter", mode = "markers+lines", 
            marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
            line = list(color = "#1E90FF80", width = 2)) %>% 
  layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))

ds2 -> ds1

Idents(ds1) <- ds1$seurat_clusters
temp <- get_data_table(ds1, highvar = F, type = "data")
ds1_data <- matrix(data=0, nrow = length(rownames(ds2_data)), ncol = length(colnames(temp)), 
                   byrow = FALSE, dimnames = list(rownames(ds2_data),colnames(temp)))
for(i in intersect(rownames(ds2_data), rownames(temp))){
  ds1_data[i,] <- temp[i,]
}
rm(temp)
ds1_label <- as.numeric(as.character(Idents(ds1)))
colnames(ds1_data) <- NULL
ds1_test_data <- list(data = t(as(ds1_data,"dgCMatrix")), label = ds1_label)
ds1_test <- xgb.DMatrix(data = ds1_test_data$data,label = ds1_test_data$label)

#预测结果

predict_ds1_test <- predict(bst_model, newdata = ds1_test)

predict_prop_ds1 <- matrix(data=predict_ds1_test, nrow = length(levels(Idents(ds2))), 
                           ncol = ncol(ds1), byrow = FALSE, 
                           dimnames = list(levels(Idents(ds2)),colnames(ds1)))

## 得到分群结果
ds1_res <- apply(predict_prop_ds1,2,func,rownames(predict_prop_ds1))
Idents(ds1) <- factor(ds1_res,levels = c(0:4))
umapplot(ds1)

ds1$supclustering <- Idents(ds1) #保存监督聚类结果

数值化地投射回umap

embedding <- FetchData(object = ds1, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds1))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`), size = 3, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`), size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`), size = 3, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
   new_scale("color") +
      geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`), size = 3, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
    new_scale("color") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("pre_ds1_umap.svg",device = svg,plot = ggobj,height = 10,width = 10)

#ds2 -> ds0

embedding <- FetchData(object = ds0, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds0))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
   new_scale("color") +
      geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
    new_scale("color") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("pre_ds1_umap.svg",device = svg,plot = ggobj,height = 10,width = 10)

PA -> AC

Idents(ds2_PA) <- ds2_PA$seurat_clusters
selected_features <- read.csv("./datatable/selected_features.csv", stringsAsFactors = F)
selected_features <- selected_features$x
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL

PA_train_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
xgb_param <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds2_PA))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_param, PA_train, nrounds = 100, verbose = 0)
embedding <- FetchData(object = ds2_AC, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_AC))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds2_PAtoAC_umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

AC to PA

Idents(ds2_AC) <- ds2_AC$seurat_clusters
selected_features <- read.csv("./datatable/selected_features.csv", stringsAsFactors = F)
selected_features <- selected_features$x
AC_data <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_data <- AC_data[selected_features,]
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
colnames(AC_data) <- NULL

AC_train_data <- list(data = t(as(AC_data,"dgCMatrix")), label = AC_label)
AC_train <- xgb.DMatrix(data = AC_train_data$data,label = AC_train_data$label)
xgb_ACram <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds2_AC))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_ACram, AC_train, nrounds = 100, verbose = 0)
Idents(ds2_PA) <- factor(ds2_PA$seurat_clusters,levels = c(0,1,2))

PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL
PA_test_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_test <- xgb.DMatrix(data = PA_test_data$data,label = PA_test_data$label)

#预测结果
predict_prop_PA <-predict(bst_model, newdata = PA_test) %>%
 matrix(nrow = length(levels(Idents(ds2_AC))), 
                           ncol = ncol(ds2_PA), byrow = FALSE, 
                           dimnames = list(levels(Idents(ds2_AC)),colnames(ds2_PA)))
PA_res <- apply(predict_prop_PA,2,func,rownames(predict_prop_PA))

confuse_matrix1 <- table(PA_test_data$label, PA_res, dnn=c("true","pre"))
sankey_plot(confuse_matrix1,session = "ACtoPA")

Idents(ds2_PA) <- factor(PA_res)
umapplot(ds2_PA)

embedding <- FetchData(object = ds2_PA, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_PA))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds2_ACtoPA_umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

在ds0上训练

Idents(ds0) <- ds0$seurat_clusters
ds0_data <- get_data_table(ds0, highvar = F, type = "data")
ds0_label <- as.numeric(as.character(Idents(ds0)))

index <- c(1:dim(ds0_data)[2]) %>% sample(ceiling(0.3*dim(ds0_data)[2]), replace = F, prob = NULL)
colnames(ds0_data) <- NULL

ds0_train_data <- list(data = t(as(ds0_data[,-index],"dgCMatrix")), label = ds0_label[-index])
ds0_test_data <- list(data = t(as(ds0_data[,index],"dgCMatrix")), label = ds0_label[index])

ds0_train <- xgb.DMatrix(data = ds0_train_data$data,label = ds0_train_data$label)
ds0_test <- xgb.DMatrix(data = ds0_test_data$data,label = ds0_test_data$label)

watchlist <- list(train = ds0_train, eval = ds0_test)
xgb_param <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds0))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_param, ds0_train, nrounds = 100, watchlist, verbose = 0)

eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>% 
  add_trace(type = "scatter", mode = "markers+lines", 
            marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
            line = list(color = "#1E90FF80", width = 2)) %>% 
  layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))
importance <- xgb.importance(colnames(ds0_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20),n_clusters = 1) + theme_bw()+theme(
    axis.title.x = element_text(size = 15), axis.text.x = element_text(size = 8, colour = "black"),
    axis.title.y = element_text(size = 15), axis.text.y = element_text(size = 12, colour = "black"),
    legend.text = element_text(size = 20), legend.title = element_blank(), panel.grid = element_blank())

write.csv(importance, "./datatable/ds0_features.csv", row.names = F)
multi_featureplot(head(importance,9)$Feature, ds0, labels = "") 
Warning: Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
Please use `as_label()` or `as_name()` instead.
This warning is displayed once per session.

ds0 -> ds2

Idents(ds2) <- ds2$seurat_clusters 
temp <- get_data_table(ds2, highvar = F, type = "data")
ds2_data <- matrix(data=0, nrow = length(rownames(ds0_data)), ncol = length(colnames(temp)), 
                   byrow = FALSE, dimnames = list(rownames(ds0_data),colnames(temp)))
for(i in intersect(rownames(ds2_data), rownames(temp))){
  ds2_data[i,] <- temp[i,]
}
rm(temp)
ds2_label <- as.numeric(as.character(Idents(ds2)))
colnames(ds2_data) <- NULL
ds2_test_data <- list(data = t(as(ds2_data,"dgCMatrix")), label = ds2_label)
ds2_test <- xgb.DMatrix(data = ds2_test_data$data,label = ds2_test_data$label)

#预测结果

predict_ds2_test <- predict(bst_model, newdata = ds2_test)

predict_prop_ds2 <- matrix(data=predict_ds2_test, nrow = bst_model[["params"]][["num_class"]], 
                           ncol = ncol(ds2), byrow = FALSE, 
                           dimnames = list(c(0:(bst_model[["params"]][["num_class"]]-1)),colnames(ds2)))

## 得到分群结果
ds2_res <- apply(predict_prop_ds2,2,func,rownames(predict_prop_ds2))
confuse_matrix1 <- table(ds2_test_data$label, ds2_res, dnn=c("true","pre"))

sankey_plot(confuse_matrix1,0:5,0:4,session = "ds0tods2")

Idents(ds2) <- factor(ds2_res,levels = c(0:5))
umapplot(ds2)

embedding <- FetchData(object = ds2, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds2))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`5`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `5`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('5', low = "#FFFFFF00", high = "#fd9999") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds0tods2umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

ds0 -> ds1

embedding <- FetchData(object = ds1, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds1))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`5`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `5`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('5', low = "#FFFFFF00", high = "#fd9999") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds0tods1umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

##lym

ARI 和聚类数的关系

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnNvdXJjZSgidGlhbmZlbmdSd3JhcHBlcnMuUiIpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkobWNsdXN0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKIyMg5pWw5YC85YyWCiMjIyBkczLorq3nu4PliIbnsbvlmagKYGBge3J9CmRzMl9kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMiwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMl9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyKSkpCgppbmRleCA8LSBjKDE6ZGltKGRzMl9kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0oZHMyX2RhdGEpWzJdKSwgcmVwbGFjZSA9IEYsIHByb2IgPSBOVUxMKQpjb2xuYW1lcyhkczJfZGF0YSkgPC0gTlVMTAoKZHMyX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczJfZGF0YVssLWluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMl9sYWJlbFstaW5kZXhdKQpkczJfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoZHMyX2RhdGFbLGluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMl9sYWJlbFtpbmRleF0pCgpkczJfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMl90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBkczJfdHJhaW5fZGF0YSRsYWJlbCkKZHMyX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMl90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGRzMl90ZXN0X2RhdGEkbGFiZWwpCgp3YXRjaGxpc3QgPC0gbGlzdCh0cmFpbiA9IGRzMl90cmFpbiwgZXZhbCA9IGRzMl90ZXN0KQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMikpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRwcm9iIiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIGRzMl90cmFpbiwgbnJvdW5kcyA9IDEwMCwgd2F0Y2hsaXN0LCB2ZXJib3NlID0gMCkKc2F2ZVJEUyhic3RfbW9kZWwsICJkczJfbW9kZWwucmRzIikKZXZhbF9sb3NzIDwtIGJzdF9tb2RlbFtbImV2YWx1YXRpb25fbG9nIl1dW1siZXZhbF9tbG9nbG9zcyJdXQpwbG90X2x5KGRhdGEuZnJhbWUoZXZhbF9sb3NzKSwgeCA9IGMoMToxMDApLCB5ID0gZXZhbF9sb3NzKSAlPiUgCiAgYWRkX3RyYWNlKHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycytsaW5lcyIsIAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgbGluZSA9IGxpc3QoY29sb3IgPSAiIzFFOTBGRkM3Iiwgd2lkdGggPSAxKSksCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkY4MCIsIHdpZHRoID0gMikpICU+JSAKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gImVwb2NoIikseWF4aXMgPSBsaXN0KHRpdGxlID0gImV2YWxfbWxvZ2xvc3MiKSkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsZmlnLndpZHRoPTZ9CmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMoZHMyX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfYncoKSt0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgZHMyIC0+IGRzMQpgYGB7cn0KSWRlbnRzKGRzMSkgPC0gZHMxJHNldXJhdF9jbHVzdGVycwp0ZW1wIDwtIGdldF9kYXRhX3RhYmxlKGRzMSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMV9kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgocm93bmFtZXMoZHMyX2RhdGEpKSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIAogICAgICAgICAgICAgICAgICAgYnlyb3cgPSBGQUxTRSwgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKGRzMl9kYXRhKSxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChyb3duYW1lcyhkczJfZGF0YSksIHJvd25hbWVzKHRlbXApKSl7CiAgZHMxX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMxX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczEpKSkKY29sbmFtZXMoZHMxX2RhdGEpIDwtIE5VTEwKZHMxX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMxX2xhYmVsKQpkczFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMxX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMxX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKCnByZWRpY3RfZHMxX3Rlc3QgPC0gcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBkczFfdGVzdCkKCnByZWRpY3RfcHJvcF9kczEgPC0gbWF0cml4KGRhdGE9cHJlZGljdF9kczFfdGVzdCwgbnJvdyA9IGxlbmd0aChsZXZlbHMoSWRlbnRzKGRzMikpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBuY29sKGRzMSksIGJ5cm93ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGV2ZWxzKElkZW50cyhkczIpKSxjb2xuYW1lcyhkczEpKSkKCiMjIOW+l+WIsOWIhue+pOe7k+aenApkczFfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9kczEsMixmdW5jLHJvd25hbWVzKHByZWRpY3RfcHJvcF9kczEpKQpJZGVudHMoZHMxKSA8LSBmYWN0b3IoZHMxX3JlcyxsZXZlbHMgPSBjKDA6NCkpCnVtYXBwbG90KGRzMSkKZHMxJHN1cGNsdXN0ZXJpbmcgPC0gSWRlbnRzKGRzMSkgI+S/neWtmOebkeedo+iBmuexu+e7k+aenApgYGAKCiMjIOaVsOWAvOWMluWcsOaKleWwhOWbnnVtYXAKYGBge3J9CmVtYmVkZGluZyA8LSBGZXRjaERhdGEob2JqZWN0ID0gZHMxLCB2YXJzID0gYygiVU1BUF8xIiwgIlVNQVBfMiIpKQplbWJlZGRpbmcgPC0gY2JpbmQoZW1iZWRkaW5nLCB0KHByZWRpY3RfcHJvcF9kczEpKQoKZ2dvYmogPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDBgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAwYCksIHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcwJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiIzZkYzBhNiIpICsKICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMWA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDFgKSxzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMScsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmIzOTgiKSArCiAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAyYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMmApLHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcyJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYTJjYSIpICsKICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgM2A+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDNgKSxzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMycsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNkMWViYTgiKSArCiAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDRgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGA0YCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzQnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjYjFkNmZiIikgKwogICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgICAgICB4bGFiKCJVTUFQIDEiKSArIHlsYWIoIlVNQVAgMiIpICArCiAgICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpKSkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBOVUxMKSArIAogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3NhdmUoInByZV9kczFfdW1hcC5zdmciLGRldmljZSA9IHN2ZyxwbG90ID0gZ2dvYmosaGVpZ2h0ID0gMTAsd2lkdGggPSAxMCkKYGBgCgojZHMyIC0+IGRzMApgYGB7cn0KSWRlbnRzKGRzMCkgPC0gZHMwJHNldXJhdF9jbHVzdGVycwp0ZW1wIDwtIGdldF9kYXRhX3RhYmxlKGRzMCwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMF9kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgocm93bmFtZXMoZHMyX2RhdGEpKSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIAogICAgICAgICAgICAgICAgICAgYnlyb3cgPSBGQUxTRSwgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKGRzMl9kYXRhKSxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChyb3duYW1lcyhkczJfZGF0YSksIHJvd25hbWVzKHRlbXApKSl7CiAgZHMwX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMwX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczApKSkKY29sbmFtZXMoZHMwX2RhdGEpIDwtIE5VTEwKZHMwX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMF9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsKQpkczBfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMwX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKCnByZWRpY3RfZHMwX3Rlc3QgPC0gcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBkczBfdGVzdCkKCnByZWRpY3RfcHJvcF9kczAgPC0gbWF0cml4KGRhdGE9cHJlZGljdF9kczBfdGVzdCwgbnJvdyA9IGxlbmd0aChsZXZlbHMoSWRlbnRzKGRzMikpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBuY29sKGRzMCksIGJ5cm93ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGV2ZWxzKElkZW50cyhkczIpKSxjb2xuYW1lcyhkczApKSkKCiMjIOW+l+WIsOWIhue+pOe7k+aenApkczBfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9kczAsMixmdW5jLHJvd25hbWVzKHByZWRpY3RfcHJvcF9kczApKQpJZGVudHMoZHMwKSA8LSBmYWN0b3IoZHMwX3JlcyxsZXZlbHMgPSBjKDA6NCkpCnVtYXBwbG90KGRzMCkKZHMwJHN1cGNsdXN0ZXJpbmcgPC0gSWRlbnRzKGRzMCkgI+S/neWtmOebkeedo+iBmuexu+e7k+aenApgYGAKCmBgYHtyfQplbWJlZGRpbmcgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IGRzMCwgdmFycyA9IGMoIlVNQVBfMSIsICJVTUFQXzIiKSkKZW1iZWRkaW5nIDwtIGNiaW5kKGVtYmVkZGluZywgdChwcmVkaWN0X3Byb3BfZHMwKSkKCmdnb2JqIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAwYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMGApLCBzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiM2ZGMwYTYiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDFgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAxYCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzEnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJiMzk4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMmA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDJgKSxzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMicsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmEyY2EiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDNgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAzYCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzMnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZDFlYmE4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGA0YD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgNGApLHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCc0JywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2IxZDZmYiIpICsKICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJwcmVfZHMwX3VtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDEwLHdpZHRoID0gMTApCmBgYAoKCiMgUEEgLT4gQUMKYGBge3J9CklkZW50cyhkczJfUEEpIDwtIGRzMl9QQSRzZXVyYXRfY2x1c3RlcnMKc2VsZWN0ZWRfZmVhdHVyZXMgPC0gcmVhZC5jc3YoIi4vZGF0YXRhYmxlL3NlbGVjdGVkX2ZlYXR1cmVzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpzZWxlY3RlZF9mZWF0dXJlcyA8LSBzZWxlY3RlZF9mZWF0dXJlcyR4ClBBX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX1BBLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKUEFfZGF0YSA8LSBQQV9kYXRhW3NlbGVjdGVkX2ZlYXR1cmVzLF0KUEFfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9QQSkpKQpjb2xuYW1lcyhQQV9kYXRhKSA8LSBOVUxMCgpQQV90cmFpbl9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoUEFfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IFBBX2xhYmVsKQpQQV90cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gUEFfdHJhaW5fZGF0YSRkYXRhLGxhYmVsID0gUEFfdHJhaW5fZGF0YSRsYWJlbCkKeGdiX3BhcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAKICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczJfUEEpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0cHJvYiIsIGV2YWxfbWV0cmljID0gJ21sb2dsb3NzJykKCmJzdF9tb2RlbCA8LSB4Z2IudHJhaW4oeGdiX3BhcmFtLCBQQV90cmFpbiwgbnJvdW5kcyA9IDEwMCwgdmVyYm9zZSA9IDApCmBgYAoKYGBge3J9CklkZW50cyhkczJfQUMpIDwtIGRzMl9BQyRzZXVyYXRfY2x1c3RlcnMKQUNfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczJfQUMsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpBQ19kYXRhIDwtIEFDX2RhdGFbc2VsZWN0ZWRfZmVhdHVyZXMsXQpBQ19sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyX0FDKSkpCmNvbG5hbWVzKEFDX2RhdGEpIDwtIE5VTEwKQUNfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoQUNfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IEFDX2xhYmVsKQpBQ190ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBBQ190ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IEFDX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKcHJlZGljdF9wcm9wX0FDIDwtcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBBQ190ZXN0KSAlPiUKIG1hdHJpeChucm93ID0gbGVuZ3RoKGxldmVscyhJZGVudHMoZHMyX1BBKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IG5jb2woZHMyX0FDKSwgYnlyb3cgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChsZXZlbHMoSWRlbnRzKGRzMl9QQSkpLGNvbG5hbWVzKGRzMl9BQykpKQpBQ19yZXMgPC0gYXBwbHkocHJlZGljdF9wcm9wX0FDLDIsZnVuYyxyb3duYW1lcyhwcmVkaWN0X3Byb3BfQUMpKQoKY29uZnVzZV9tYXRyaXgxIDwtIHRhYmxlKEFDX3Rlc3RfZGF0YSRsYWJlbCwgQUNfcmVzLCBkbm49YygidHJ1ZSIsInByZSIpKQpzYW5rZXlfcGxvdChjb25mdXNlX21hdHJpeDEsc2Vzc2lvbiA9ICJQQXRvQUMiKQoKSWRlbnRzKGRzMl9BQykgPC0gZmFjdG9yKEFDX3JlcyxsZXZlbHMgPSBjKDA6MikpCnVtYXBwbG90KGRzMl9BQykKYGBgCgpgYGB7cn0KZW1iZWRkaW5nIDwtIEZldGNoRGF0YShvYmplY3QgPSBkczJfQUMsIHZhcnMgPSBjKCJVTUFQXzEiLCAiVU1BUF8yIikpCmVtYmVkZGluZyA8LSBjYmluZChlbWJlZGRpbmcsIHQocHJlZGljdF9wcm9wX0FDKSkKCmdnb2JqIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAwYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMGApLCBzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiM2ZGMwYTYiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDFgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAxYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzEnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJiMzk4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMmA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDJgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMicsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmEyY2EiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJkczJfUEF0b0FDX3VtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDgsd2lkdGggPSA4KQpgYGAKCgoKIyMgQUMgdG8gUEEKYGBge3J9CklkZW50cyhkczJfQUMpIDwtIGRzMl9BQyRzZXVyYXRfY2x1c3RlcnMKc2VsZWN0ZWRfZmVhdHVyZXMgPC0gcmVhZC5jc3YoIi4vZGF0YXRhYmxlL3NlbGVjdGVkX2ZlYXR1cmVzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpzZWxlY3RlZF9mZWF0dXJlcyA8LSBzZWxlY3RlZF9mZWF0dXJlcyR4CkFDX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKQUNfZGF0YSA8LSBBQ19kYXRhW3NlbGVjdGVkX2ZlYXR1cmVzLF0KQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9BQykpKQpjb2xuYW1lcyhBQ19kYXRhKSA8LSBOVUxMCgpBQ190cmFpbl9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoQUNfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IEFDX2xhYmVsKQpBQ190cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gQUNfdHJhaW5fZGF0YSRkYXRhLGxhYmVsID0gQUNfdHJhaW5fZGF0YSRsYWJlbCkKeGdiX0FDcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAKICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczJfQUMpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0cHJvYiIsIGV2YWxfbWV0cmljID0gJ21sb2dsb3NzJykKCmJzdF9tb2RlbCA8LSB4Z2IudHJhaW4oeGdiX0FDcmFtLCBBQ190cmFpbiwgbnJvdW5kcyA9IDEwMCwgdmVyYm9zZSA9IDApCmBgYAoKYGBge3J9CklkZW50cyhkczJfUEEpIDwtIGZhY3RvcihkczJfUEEkc2V1cmF0X2NsdXN0ZXJzLGxldmVscyA9IGMoMCwxLDIpKQoKUEFfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczJfUEEsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpQQV9kYXRhIDwtIFBBX2RhdGFbc2VsZWN0ZWRfZmVhdHVyZXMsXQpQQV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyX1BBKSkpCmNvbG5hbWVzKFBBX2RhdGEpIDwtIE5VTEwKUEFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoUEFfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IFBBX2xhYmVsKQpQQV90ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBQQV90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IFBBX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKcHJlZGljdF9wcm9wX1BBIDwtcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBQQV90ZXN0KSAlPiUKIG1hdHJpeChucm93ID0gbGVuZ3RoKGxldmVscyhJZGVudHMoZHMyX0FDKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IG5jb2woZHMyX1BBKSwgYnlyb3cgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChsZXZlbHMoSWRlbnRzKGRzMl9BQykpLGNvbG5hbWVzKGRzMl9QQSkpKQpQQV9yZXMgPC0gYXBwbHkocHJlZGljdF9wcm9wX1BBLDIsZnVuYyxyb3duYW1lcyhwcmVkaWN0X3Byb3BfUEEpKQoKY29uZnVzZV9tYXRyaXgxIDwtIHRhYmxlKFBBX3Rlc3RfZGF0YSRsYWJlbCwgUEFfcmVzLCBkbm49YygidHJ1ZSIsInByZSIpKQpzYW5rZXlfcGxvdChjb25mdXNlX21hdHJpeDEsc2Vzc2lvbiA9ICJBQ3RvUEEiKQoKSWRlbnRzKGRzMl9QQSkgPC0gZmFjdG9yKFBBX3JlcykKdW1hcHBsb3QoZHMyX1BBKQpgYGAKCmBgYHtyfQplbWJlZGRpbmcgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IGRzMl9QQSwgdmFycyA9IGMoIlVNQVBfMSIsICJVTUFQXzIiKSkKZW1iZWRkaW5nIDwtIGNiaW5kKGVtYmVkZGluZywgdChwcmVkaWN0X3Byb3BfUEEpKQoKZ2dvYmogPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDBgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAwYCksIHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcwJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiIzZkYzBhNiIpICsKICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMWA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDFgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMScsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmIzOTgiKSArCiAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAyYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMmApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcyJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYTJjYSIpICsKICAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgM2A+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDNgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMycsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNkMWViYTgiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJkczJfQUN0b1BBX3VtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDgsd2lkdGggPSA4KQpgYGAKCgojIyDlnKhkczDkuIrorq3nu4MKYGBge3J9CklkZW50cyhkczApIDwtIGRzMCRzZXVyYXRfY2x1c3RlcnMKZHMwX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKZHMwX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczApKSkKCmluZGV4IDwtIGMoMTpkaW0oZHMwX2RhdGEpWzJdKSAlPiUgc2FtcGxlKGNlaWxpbmcoMC4zKmRpbShkczBfZGF0YSlbMl0pLCByZXBsYWNlID0gRiwgcHJvYiA9IE5VTEwpCmNvbG5hbWVzKGRzMF9kYXRhKSA8LSBOVUxMCgpkczBfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMF9kYXRhWywtaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsWy1pbmRleF0pCmRzMF90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczBfZGF0YVssaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsW2luZGV4XSkKCmRzMF90cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IGRzMF90cmFpbl9kYXRhJGxhYmVsKQpkczBfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMwX3Rlc3RfZGF0YSRsYWJlbCkKCndhdGNobGlzdCA8LSBsaXN0KHRyYWluID0gZHMwX3RyYWluLCBldmFsID0gZHMwX3Rlc3QpCnhnYl9wYXJhbSA8LSBsaXN0KGV0YSA9IDAuMiwgbWF4X2RlcHRoID0gNiwgCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMoZHMwKSkpLAogICAgICAgICAgICAgICAgICBvYmplY3RpdmUgPSAibXVsdGk6c29mdHByb2IiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgZHMwX3RyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QsIHZlcmJvc2UgPSAwKQoKZXZhbF9sb3NzIDwtIGJzdF9tb2RlbFtbImV2YWx1YXRpb25fbG9nIl1dW1siZXZhbF9tbG9nbG9zcyJdXQpwbG90X2x5KGRhdGEuZnJhbWUoZXZhbF9sb3NzKSwgeCA9IGMoMToxMDApLCB5ID0gZXZhbF9sb3NzKSAlPiUgCiAgYWRkX3RyYWNlKHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycytsaW5lcyIsIAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgbGluZSA9IGxpc3QoY29sb3IgPSAiIzFFOTBGRkM3Iiwgd2lkdGggPSAxKSksCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkY4MCIsIHdpZHRoID0gMikpICU+JSAKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gImVwb2NoIikseWF4aXMgPSBsaXN0KHRpdGxlID0gImV2YWxfbWxvZ2xvc3MiKSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTZ9CmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMoZHMwX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfYncoKSt0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkKd3JpdGUuY3N2KGltcG9ydGFuY2UsICIuL2RhdGF0YWJsZS9kczBfZmVhdHVyZXMuY3N2Iiwgcm93Lm5hbWVzID0gRikKbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDkpJEZlYXR1cmUsIGRzMCwgbGFiZWxzID0gIiIpIApgYGAKIyMgZHMwIC0+IGRzMgpgYGB7cn0KSWRlbnRzKGRzMikgPC0gZHMyJHNldXJhdF9jbHVzdGVycyAKdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczIsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpkczJfZGF0YSA8LSBtYXRyaXgoZGF0YT0wLCBucm93ID0gbGVuZ3RoKHJvd25hbWVzKGRzMF9kYXRhKSksIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVtcCkpLCAKICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gRkFMU0UsIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhkczBfZGF0YSksY29sbmFtZXModGVtcCkpKQpmb3IoaSBpbiBpbnRlcnNlY3Qocm93bmFtZXMoZHMyX2RhdGEpLCByb3duYW1lcyh0ZW1wKSkpewogIGRzMl9kYXRhW2ksXSA8LSB0ZW1wW2ksXQp9CnJtKHRlbXApCmRzMl9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyKSkpCmNvbG5hbWVzKGRzMl9kYXRhKSA8LSBOVUxMCmRzMl90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczJfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMl9sYWJlbCkKZHMyX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMl90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGRzMl90ZXN0X2RhdGEkbGFiZWwpCgoj6aKE5rWL57uT5p6cCgpwcmVkaWN0X2RzMl90ZXN0IDwtIHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gZHMyX3Rlc3QpCgpwcmVkaWN0X3Byb3BfZHMyIDwtIG1hdHJpeChkYXRhPXByZWRpY3RfZHMyX3Rlc3QsIG5yb3cgPSBic3RfbW9kZWxbWyJwYXJhbXMiXV1bWyJudW1fY2xhc3MiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbmNvbChkczIpLCBieXJvdyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoMDooYnN0X21vZGVsW1sicGFyYW1zIl1dW1sibnVtX2NsYXNzIl1dLTEpKSxjb2xuYW1lcyhkczIpKSkKCiMjIOW+l+WIsOWIhue+pOe7k+aenApkczJfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9kczIsMixmdW5jLHJvd25hbWVzKHByZWRpY3RfcHJvcF9kczIpKQpjb25mdXNlX21hdHJpeDEgPC0gdGFibGUoZHMyX3Rlc3RfZGF0YSRsYWJlbCwgZHMyX3JlcywgZG5uPWMoInRydWUiLCJwcmUiKSkKCnNhbmtleV9wbG90KGNvbmZ1c2VfbWF0cml4MSwwOjUsMDo0LHNlc3Npb24gPSAiZHMwdG9kczIiKQoKSWRlbnRzKGRzMikgPC0gZmFjdG9yKGRzMl9yZXMsbGV2ZWxzID0gYygwOjUpKQp1bWFwcGxvdChkczIpCgpgYGAKCmBgYHtyfQplbWJlZGRpbmcgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IGRzMiwgdmFycyA9IGMoIlVNQVBfMSIsICJVTUFQXzIiKSkKZW1iZWRkaW5nIDwtIGNiaW5kKGVtYmVkZGluZywgdChwcmVkaWN0X3Byb3BfZHMyKSkKCmdnb2JqIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAwYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMGApLCBzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiM2ZGMwYTYiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDFgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAxYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzEnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJiMzk4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMmA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDJgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMicsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmEyY2EiKSArCiAgICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDNgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAzYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzMnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZDFlYmE4IikgKwogICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGA0YD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgNGApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCc0JywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2IxZDZmYiIpICsKICAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgNWA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDVgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnNScsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNmZDk5OTkiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJkczB0b2RzMnVtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDgsd2lkdGggPSA4KQpgYGAKCiMjIGRzMCAtPiBkczEKYGBge3J9CklkZW50cyhkczEpIDwtIGRzMSRzZXVyYXRfY2x1c3RlcnMKdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczEsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpkczFfZGF0YSA8LSBtYXRyaXgoZGF0YT0wLCBucm93ID0gbGVuZ3RoKHJvd25hbWVzKGRzMF9kYXRhKSksIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVtcCkpLCAKICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gRkFMU0UsIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhkczBfZGF0YSksY29sbmFtZXModGVtcCkpKQpmb3IoaSBpbiBpbnRlcnNlY3Qocm93bmFtZXMoZHMxX2RhdGEpLCByb3duYW1lcyh0ZW1wKSkpewogIGRzMV9kYXRhW2ksXSA8LSB0ZW1wW2ksXQp9CnJtKHRlbXApCmRzMV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMxKSkpCmNvbG5hbWVzKGRzMV9kYXRhKSA8LSBOVUxMCmRzMV90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczFfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMV9sYWJlbCkKZHMxX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMV90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGRzMV90ZXN0X2RhdGEkbGFiZWwpCgoj6aKE5rWL57uT5p6cCgpwcmVkaWN0X2RzMV90ZXN0IDwtIHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gZHMxX3Rlc3QpCgpwcmVkaWN0X3Byb3BfZHMxIDwtIG1hdHJpeChkYXRhPXByZWRpY3RfZHMxX3Rlc3QsIG5yb3cgPSBic3RfbW9kZWxbWyJwYXJhbXMiXV1bWyJudW1fY2xhc3MiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbmNvbChkczEpLCBieXJvdyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoMDooYnN0X21vZGVsW1sicGFyYW1zIl1dW1sibnVtX2NsYXNzIl1dLTEpKSxjb2xuYW1lcyhkczEpKSkKCiMjIOW+l+WIsOWIhue+pOe7k+aenApkczFfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9kczEsMixmdW5jLHJvd25hbWVzKHByZWRpY3RfcHJvcF9kczEpKQpJZGVudHMoZHMxKSA8LSBmYWN0b3IoZHMxX3JlcyxsZXZlbHMgPSBjKDA6NSkpCnVtYXBwbG90KGRzMSkKCmNvbmZ1c2VfbWF0cml4IDwtIHRhYmxlKGRzMV90ZXN0X2RhdGEkbGFiZWwsIGRzMV9yZXMsIGRubj1jKCJ0cnVlIiwicHJlIikpCnNhbmtleV9wbG90KGNvbmZ1c2VfbWF0cml4LGMoMDo0KSxjKDA6NCksc2Vzc2lvbiA9ICJkczB0b2RzMSIpCmBgYAoKYGBge3J9CmVtYmVkZGluZyA8LSBGZXRjaERhdGEob2JqZWN0ID0gZHMxLCB2YXJzID0gYygiVU1BUF8xIiwgIlVNQVBfMiIpKQplbWJlZGRpbmcgPC0gY2JpbmQoZW1iZWRkaW5nLCB0KHByZWRpY3RfcHJvcF9kczEpKQoKZ2dvYmogPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDBgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAwYCksIHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcwJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiIzZkYzBhNiIpICsKICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMWA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDFgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMScsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmIzOTgiKSArCiAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAyYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMmApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcyJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYTJjYSIpICsKICAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgM2A+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDNgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMycsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNkMWViYTgiKSArCiAgICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDRgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGA0YCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzQnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjYjFkNmZiIikgKwogICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGA1YD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgNWApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCc1JywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2ZkOTk5OSIpICsKICAgICAgICB4bGFiKCJVTUFQIDEiKSArIHlsYWIoIlVNQVAgMiIpICArCiAgICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpKSkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBOVUxMKSArIAogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3NhdmUoImRzMHRvZHMxdW1hcC5zdmciLGRldmljZSA9IHN2ZyxwbG90ID0gZ2dvYmosaGVpZ2h0ID0gOCx3aWR0aCA9IDgpCmBgYAoKCgojI2x5bQpgYGB7cn0KCmBgYAoKCiMjIEFSSSDlkozogZrnsbvmlbDnmoTlhbPns7sKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==